/*
 * Copyright (c) 2010 - 2011, Brookhaven National Laboratory
 * All rights reserved.
 * The software in this package is published under the terms
 * of the BSD license a copy of which has been included with
 * this distribution in the license.txt file.
 */

package org.oxtypes.impl;

import org.oxtypes.impl.modifiable.ModifiableTypeDescriptorImpl;
import java.util.Map;
import java.util.TreeMap;

import org.omg.dds.type.TypeKind;
import org.omg.dds.type.dynamic.DynamicType;
import org.omg.dds.type.dynamic.TypeDescriptor;

/**
 *
 */
public class DynamicTypeManagerImpl {

    static public DynamicTypeManagerImpl getInstance(){
        if(singleton == null){
            singleton = new DynamicTypeManagerImpl();
        }
        return singleton;
    }

    public DynamicType getPrimitiveType(TypeKind kind){
        switch(kind){
            case BOOLEAN_TYPE: return boolType;
            case BYTE_TYPE: return byteType;
            case INT_16_TYPE: return int16Type;
            case UINT_16_TYPE: return uint16Type;
            case INT_32_TYPE: return int32Type;
            case UINT_32_TYPE: return uint32Type;
            case INT_64_TYPE: return int64Type;
            case UINT_64_TYPE: return uint64Type;
            case FLOAT_32_TYPE: return float32Type;
            case FLOAT_64_TYPE: return float64Type;
            case FLOAT_128_TYPE: return float128Type;
            case CHAR_8_TYPE: return char8Type;
            case CHAR_32_TYPE: return char32Type;
            default: return null;
        }
    }

    public DynamicType createSequenceType(DynamicType elementType, int bound){

        // check arguments

        if(elementType == null) {
            throw new java.lang.IllegalArgumentException("element type is null");
        }
        if(bound < 0) {
            throw new java.lang.IllegalArgumentException("bound < 0");
        }

        // create a type name

        StringBuilder buffer = new StringBuilder("sequence_");
        if(bound != 0) {
            buffer.append(bound);
            buffer.append("_");
        }
        buffer.append(elementType.getName());

        String typeName = buffer.toString();

        // check registry

        DynamicType result = sequenceTypes.get(typeName);
        if(result != null) return result;

        result = createSequenceType(typeName, elementType, bound);

        sequenceTypes.put(typeName, result);
        types.put(typeName, result);

        return result;
    }

    public DynamicType loadType(DynamicType type){
        switch(type.getKind()){
            case UNION_TYPE:
            case STRUCTURE_TYPE:
            case ANNOTATION_TYPE: types.put(type.getName(), type); return type;
            default: return null;
        }
    }

    public DynamicType getType(String name){
        return types.get(name);
    }

    private DynamicTypeManagerImpl() {
        
        types = new TreeMap<String, DynamicType>();

        types.put(boolType.getName(), boolType);
        types.put(byteType.getName(), byteType);
        types.put(int16Type.getName(), int16Type);
        types.put(uint16Type.getName(), uint16Type);
        types.put(int32Type.getName(), int32Type);
        types.put(uint32Type.getName(), uint32Type);
        types.put(int64Type.getName(), int64Type);
        types.put(uint64Type.getName(), uint64Type);
        types.put(float32Type.getName(), float32Type);
        types.put(float64Type.getName(), float64Type);
        types.put(float128Type.getName(), float128Type);
        types.put(char8Type.getName(), char8Type);
        types.put(char32Type.getName(), char32Type);

        sequenceTypes = new TreeMap<String, DynamicType>();
    }

    static private DynamicTypeManagerImpl singleton;

    private Map<String, DynamicType> types;

    // Primitive Types

    final static DynamicType boolType;
    final static DynamicType byteType;
    final static DynamicType int16Type;
    final static DynamicType uint16Type;
    final static DynamicType int32Type;
    final static DynamicType uint32Type;
    final static DynamicType int64Type;
    final static DynamicType uint64Type;
    final static DynamicType float32Type;
    final static DynamicType float64Type;
    final static DynamicType float128Type;
    final static DynamicType char8Type;
    final static DynamicType char32Type;

    // Sequence Types

    Map<String, DynamicType> sequenceTypes;

    static {

        ModifiableTypeDescriptorImpl boolDescriptor = new ModifiableTypeDescriptorImpl();
        boolDescriptor.setName("Boolean");
        boolDescriptor.setKind(TypeKind.BOOLEAN_TYPE);
        boolType = new DynamicTypeImpl(boolDescriptor, null, null);

        ModifiableTypeDescriptorImpl byteDescriptor = new ModifiableTypeDescriptorImpl();
        byteDescriptor.setName("Byte");
        byteDescriptor.setKind(TypeKind.BYTE_TYPE);
        byteType = new DynamicTypeImpl(byteDescriptor, null, null);

        ModifiableTypeDescriptorImpl int16Descriptor = new ModifiableTypeDescriptorImpl();
        int16Descriptor.setName("Int16");
        int16Descriptor.setKind(TypeKind.INT_16_TYPE);
        int16Type = new DynamicTypeImpl(int16Descriptor, null, null);

        ModifiableTypeDescriptorImpl uint16Descriptor = new ModifiableTypeDescriptorImpl();
        uint16Descriptor.setName("UInt16");
        uint16Descriptor.setKind(TypeKind.UINT_16_TYPE);
        uint16Type = new DynamicTypeImpl(uint16Descriptor, null, null);

        ModifiableTypeDescriptorImpl int32Descriptor = new ModifiableTypeDescriptorImpl();
        int32Descriptor.setName("Int32");
        int32Descriptor.setKind(TypeKind.INT_32_TYPE);
        int32Type = new DynamicTypeImpl(int32Descriptor, null, null);

        ModifiableTypeDescriptorImpl uint32Descriptor = new ModifiableTypeDescriptorImpl();
        uint32Descriptor.setName("UInt32");
        uint32Descriptor.setKind(TypeKind.UINT_32_TYPE);
        uint32Type = new DynamicTypeImpl(uint32Descriptor, null, null);

        ModifiableTypeDescriptorImpl int64Descriptor = new ModifiableTypeDescriptorImpl();
        int64Descriptor.setName("Int64");
        int64Descriptor.setKind(TypeKind.INT_64_TYPE);
        int64Type = new DynamicTypeImpl(int64Descriptor, null, null);

        ModifiableTypeDescriptorImpl uint64Descriptor = new ModifiableTypeDescriptorImpl();
        uint64Descriptor.setName("UInt64");
        uint64Descriptor.setKind(TypeKind.UINT_64_TYPE);
        uint64Type = new DynamicTypeImpl(uint64Descriptor, null, null);

        ModifiableTypeDescriptorImpl float32Descriptor = new ModifiableTypeDescriptorImpl();
        float32Descriptor.setName("Float32");
        float32Descriptor.setKind(TypeKind.FLOAT_32_TYPE);
        float32Type = new DynamicTypeImpl(float32Descriptor, null, null);

        ModifiableTypeDescriptorImpl float64Descriptor = new ModifiableTypeDescriptorImpl();
        float64Descriptor.setName("Float64");
        float64Descriptor.setKind(TypeKind.FLOAT_64_TYPE);
        float64Type = new DynamicTypeImpl(float64Descriptor, null, null);

        ModifiableTypeDescriptorImpl float128Descriptor = new ModifiableTypeDescriptorImpl();
        float128Descriptor.setName("Float128");
        float128Descriptor.setKind(TypeKind.FLOAT_128_TYPE);
        float128Type = new DynamicTypeImpl(float128Descriptor, null, null);

        ModifiableTypeDescriptorImpl char8Descriptor = new ModifiableTypeDescriptorImpl();
        char8Descriptor.setName("Char8");
        char8Descriptor.setKind(TypeKind.CHAR_8_TYPE);
        char8Type = new DynamicTypeImpl(char8Descriptor, null, null);

        ModifiableTypeDescriptorImpl char32Descriptor = new ModifiableTypeDescriptorImpl();
        char32Descriptor.setName("Char32");
        char32Descriptor.setKind(TypeKind.CHAR_32_TYPE);
        char32Type = new DynamicTypeImpl(char32Descriptor, null, null);
    }

    private DynamicType createSequenceType(String typeName, DynamicType elementType, int bound){

        ModifiableTypeDescriptorImpl seqDescriptor = new ModifiableTypeDescriptorImpl();

        seqDescriptor.setName(typeName);
        seqDescriptor.setKind(TypeKind.SEQUENCE_TYPE);
        seqDescriptor.setElementType(elementType);
        seqDescriptor.setBound(bound);

        return new DynamicTypeImpl(seqDescriptor, null, null);
    }

}
